package com.celesea.configuration.security;

import com.alibaba.fastjson.JSON;
import org.jasig.cas.client.session.RedisBackedSessionMappingStorage;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private static Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Autowired
    private SecurityPermitProperties permitProperties;

    @Autowired
    private CasRedisProperties casRedisProperties;

    @Autowired
    private CasProperties casProperties;

    @Autowired
    private CorsProperties corsProperties;

    @Autowired
    private RedisAuthenticationFilter redisAuthenticationFilter;

    /**
     * 定义安全策略
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.csrf()
//                .disable()
//                .authorizeRequests()//配置安全策略
//                //.antMatchers("/innerApi/**").permitAll()//定义请求不需要验证
//                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
//                .anyRequest().authenticated()//其余的所有请求都需要验证
//                .and()
//                .logout()
//                .permitAll()//定义logout不需要验证
//                .and()
//                .formLogin();//使用form表单登录
//
//        http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())
//                .and()
//                .addFilterBefore(corsFilter(), CasAuthenticationFilter.class)
//                .addFilter(casAuthenticationFilter())
//                .addFilterBefore(casLogoutFilter(), LogoutFilter.class)
//                .addFilterBefore(redisAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
//                .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        logger.info("permitUrls:{}", JSON.toJSONString(permitProperties.getUrl()));
//        web.ignoring().antMatchers(permitProperties.toArray());
    }

    /**
     * 认证的入口
     */
    @Bean
    public AuthenticationEntryPoint casAuthenticationEntryPoint() {
        CustomCasAuthenticationEntryPoint casEntryPoint = new CustomCasAuthenticationEntryPoint();
//      CasAuthenticationEntryPoint casEntryPoint = new CasAuthenticationEntryPoint();
        casEntryPoint.setLoginUrl(casProperties.getServerHost() + casProperties.getServerLogin());
        casEntryPoint.setServiceProperties(serviceProperties());
        logger.info("loginUrl:{}", casEntryPoint.getLoginUrl());
        logger.info("properties:{}", JSON.toJSONString(casEntryPoint.getServiceProperties()));
        return casEntryPoint;
    }

    /**
     * 指定service相关信息
     */
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        // 设置回调的service路径，此为主页路径
        serviceProperties.setService(casProperties.getClientHost() + casProperties.getClientLogin());
        // 对所有的未拥有ticket的访问均需要验证
        serviceProperties.setAuthenticateAllArtifacts(true);
        return serviceProperties;
    }

    /**
     * CAS认证过滤器
     */
    @Bean
    public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());
        casAuthenticationFilter.setFilterProcessesUrl(casProperties.getClientLogin());
        logger.info("setFilterProcessesUrl:{}", casProperties.getClientLogin());
        return casAuthenticationFilter;
    }

    /**
     * cas 认证 Provider
     */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
        casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
        casAuthenticationProvider.setKey("casAuthenticationProviderKey");
        return casAuthenticationProvider;
    }


    /**
     * 自定义的AuthenticationUserDetailsService
     */
    @Bean
    public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() {
        return new RedisUserDetailsService();
    }

    @Bean
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
        // 配置上服务端的校验ticket地址
        return new Cas20ServiceTicketValidator(casProperties.getServerHost());
    }

    /**
     * 单点注销，接受cas服务端发出的注销session请求
     */
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casProperties.getServerHost());
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        logger.info("setCasServerUrlPrefix:{}", casProperties.getServerHost());
        return singleSignOutFilter;
    }

    /**
     * 单点请求cas客户端退出Filter类
     * <p>
     * 请求/logout，转发至cas服务端进行注销
     */
    @Bean
    public LogoutFilter casLogoutFilter() {
        StringBuilder logoutRedirectPath = new StringBuilder();
        logoutRedirectPath.append(casProperties.getServerHost())
                .append(casProperties.getServerLogout())
                .append("?service=")
                .append(casProperties.getClientHost() + casProperties.getApplicationName() + "/index");

        LogoutFilter logoutFilter = new LogoutFilter(logoutRedirectPath.toString(), new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casProperties.getClientLogout());

        logger.info("logout:{};setFilterProcessesUrl:{}", logoutRedirectPath, casProperties.getClientLogout());
        return logoutFilter;
    }

    /**
     * 配置跨域访问
     *
     * @return
     */
    public CorsConfigurationSource corsConfigurationSource() {
        logger.info(JSON.toJSONString(corsProperties));
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(corsProperties.getOrigins());
        configuration.setAllowedMethods(corsProperties.getMethods());
        configuration.setAllowedHeaders(corsProperties.getHeaders());
        configuration.setMaxAge(corsProperties.getMaxAge());
        configuration.setAllowCredentials(corsProperties.isCredentials());

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        return new CorsFilter(corsConfigurationSource());
    }

    /***************** configuration cas redis ***************/

    @Bean("casConnectionFactory")
    public JedisConnectionFactory casConnectionFactory() {
        logger.info("cas redis info:{}", JSON.toJSONString(casRedisProperties));
        return casRedisProperties.connectionFactory();
    }

    @Bean("casRedisTemplate")
    public StringRedisTemplate casRedisTemplate(
//            @Qualifier("casConnectionFactory")RedisConnectionFactory casConnectionFactory
    ) {
        RedisConnectionFactory casConnectionFactory = casConnectionFactory();
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(casConnectionFactory);
        return template;
    }
}
